#include <stdbool.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <pulse/pulseaudio.h>
#include <math.h>
#include "logger.h"
#include "pcm-player.h"

static pa_simple *pa_handle = NULL;
static pa_sample_spec sample_spec;
static pa_mainloop *mainloop = NULL;
static pa_context *context = NULL;
static pa_cvolume volume;
static uint32_t stream_index = PA_INVALID_INDEX;
static player_ctrl_cmd_t current_state = PLAYER_CMD_STOP;

// Initialize PulseAudio context
static void init_pulseaudio_context() {
    mainloop = pa_mainloop_new();
    if (!mainloop) {
        elog("Failed to create PulseAudio mainloop\n");
        return;
    }

    context = pa_context_new(pa_mainloop_get_api(mainloop), "PCM Player");
    if (!context) {
        elog("Failed to create PulseAudio context\n");
        pa_mainloop_free(mainloop);
        mainloop = NULL;
        return;
    }

    pa_context_connect(context, NULL, 0, NULL);
    
    // Wait for context to be ready
    while (true) {
        pa_context_state_t state = pa_context_get_state(context);
        if (state == PA_CONTEXT_READY) {
            break;
        }
        
        if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED) {
            elog("Failed to connect PulseAudio context\n");
            return;
        }
        
        int retval;
        if (pa_mainloop_iterate(mainloop, 1, &retval) < 0) {
            elog("pa_mainloop_iterate error\n");
            break;
        }
    }
}

// Get current playback stream index
static void get_stream_index_callback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) {
    if (eol > 0) {
        return;
    }
    stream_index = i->index;
    volume = i->volume;
}

static void get_stream_index(void) {
    // Get current playback stream index
    pa_operation *op = pa_context_get_sink_input_info_list(context, get_stream_index_callback, NULL);
    if (!op) {
        elog("Failed to get sink input info list\n");
        return;
    }

    // Wait for operation to complete
    while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) {
        pa_mainloop_iterate(mainloop, 1, NULL);
    }
    dlog("Stream index: %d\n", stream_index);

    // Free operation
    pa_operation_unref(op);
}

static int check_pulseaudio_support() {
    pa_context *tmp_context;
    pa_mainloop *tmp_mainloop;

    // Create mainloop and context
    tmp_mainloop = pa_mainloop_new();
    if (!tmp_mainloop) {
        elog("PulseAudio mainloop creation failed\n");
        return 0;
    }

    tmp_context = pa_context_new(pa_mainloop_get_api(tmp_mainloop), "test");
    if (!tmp_context) {
        dlog("PulseAudio context creation failed\n");
        pa_mainloop_free(tmp_mainloop);
        return 0;
    }

    // Try to connect to PulseAudio server
    if (pa_context_connect(tmp_context, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) {
        dlog("PulseAudio not supported or server not running\n");
        pa_context_unref(tmp_context);
        pa_mainloop_free(tmp_mainloop);
        return 0;
    }

    // Cleanup
    pa_context_disconnect(tmp_context);
    pa_context_unref(tmp_context);
    pa_mainloop_free(tmp_mainloop);
    dlog("PulseAudio is supported!\n");
    return 1;
}

static int pcmPlayer_setVolume(int db);

static int pcmPlayer_init(int channels, int samplerate, int bits_per_sample) {
    int error;
    
    // Configure sample parameters
    sample_spec.channels = channels;
    sample_spec.rate = samplerate;
    sample_spec.format = PA_SAMPLE_S16LE;

    // Configure buffer parameters
    pa_buffer_attr buffer_attr = {
        .maxlength = (uint32_t)-1,
        .tlength = (uint32_t)(samplerate * 0.1), // 100ms buffer
        .prebuf = (uint32_t)-1,
        .minreq = (uint32_t)-1,
        .fragsize = (uint32_t)-1
    };

    // Initialize volume control
    init_pulseaudio_context();
    pa_cvolume_init(&volume);
    pa_cvolume_set(&volume, channels, PA_VOLUME_NORM);

    // Open audio stream
    pa_handle = pa_simple_new(NULL,
                             "PCM Player",
                             PA_STREAM_PLAYBACK,
                             NULL,
                             "playback",
                             &sample_spec,
                             NULL,
                             &buffer_attr,
                             &error);

    if (!pa_handle) {
        elog("pa_simple_new error: %s\n", pa_strerror(error));
        return -1;
    }

    // Get current playback stream index
    get_stream_index();

    int def_vol = pcmPlayer_setVolume(0);

    dlog("Default volume: %d\n", def_vol);
    current_state = PLAYER_CMD_STOP;

    ilog("PulseAudio player initialized (channels=%d, rate=%d)\n", 
         channels, samplerate);
    return 0;
}

// Volume control callback
static void set_volume_callback(pa_context *c, int success, void *userdata) {
    *(int*)userdata = success; // Store operation success status
}

static int pcmPlayer_setVolume(int db) {
    if (!context || !mainloop || stream_index == PA_INVALID_INDEX) {
        return -1;
    }
    pa_operation *op = pa_context_get_sink_input_info(context, stream_index, get_stream_index_callback, NULL);
    if (!op) {
        elog("Failed to get sink input info\n");
        return -1;
    }

    // Wait for operation to complete
    while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) {
        pa_mainloop_iterate(mainloop, 1, NULL);
    }
    pa_operation_unref(op);

    // Convert dB to PA volume value
    pa_volume_t vol = pa_cvolume_avg(&volume);
    double volume_multiplier = pow(10, db / 20.0);
    pa_volume_t new_vol = vol * volume_multiplier;
    if (db > 0 && new_vol == vol) new_vol++; // Avoid gain value of 0

    // Limit to valid range
    if (new_vol > PA_VOLUME_NORM)
        new_vol = PA_VOLUME_NORM;
    if (new_vol < PA_VOLUME_MUTED)
        new_vol = PA_VOLUME_MUTED;

    if(new_vol != vol) {
        // Set volume for all channels
        pa_cvolume_set(&volume, sample_spec.channels, new_vol);
        
        int success = 0;
        pa_operation *vol_op = pa_context_set_sink_input_volume(context, stream_index, &volume, set_volume_callback, &success);
        if (!vol_op) {
            elog("Failed to set volume\n");
            return -1;
        }

        // Wait for operation to complete
        while (pa_operation_get_state(vol_op) == PA_OPERATION_RUNNING) {
            pa_mainloop_iterate(mainloop, 1, NULL);
        }
        pa_operation_unref(vol_op);

        dlog("Volume value set to %d\n", new_vol);
    }

    // Calculate and return normalized volume value (0-100)
    return (int)((new_vol - PA_VOLUME_MUTED) * 100 / (PA_VOLUME_NORM - PA_VOLUME_MUTED));
}

static int pcmPlayer_write(void *pcm_data, int size) {
    if (!pa_handle) {
        elog("pa_handle is NULL\n");
        return -1;
    }

    int error;
    if (pa_simple_write(pa_handle, pcm_data, size, &error) < 0) {
        elog("pa_simple_write error: %s\n", pa_strerror(error));
        return -1;
    }
    return 0;
}

static int pcmPlayer_getParams(int *channels, int *samplerate, int *bits_per_sample) {
    if (!channels || !samplerate || !bits_per_sample) {
        elog("Invalid parameter pointers\n");
        return -1;
    }
    
    *channels = sample_spec.channels;
    *samplerate = sample_spec.rate;
    *bits_per_sample = 16; // Corresponds to PA_SAMPLE_S16LE
    return 0;
}

/**
 * 控制PCM播放器（播放/暂停/停止）
 */
static int pcmPlayer_ctrl(player_ctrl_cmd_t cmd, void *arg) {
    int error = 0;
    
    if (cmd == current_state) {
        return 0; // Already in requested state
    }

    switch (cmd) {
        case PLAYER_CMD_RESUME:
            dlog("PulseAudio player: Resume\n");
            current_state = PLAYER_CMD_RESUME;
            break;
            
        case PLAYER_CMD_PAUSE:
            dlog("PulseAudio player: Pause\n");
            if (pa_handle) {
                if (pa_simple_flush(pa_handle, &error) < 0) {
                    elog("pa_simple_flush error: %s\n", pa_strerror(error));
                    return -1;
                }
            }
            current_state = PLAYER_CMD_PAUSE;
            break;
            
        case PLAYER_CMD_STOP:
            dlog("PulseAudio player: Stop\n");
            if (pa_handle) {
                if (pa_simple_flush(pa_handle, &error) < 0) {
                    elog("pa_simple_flush error: %s\n", pa_strerror(error));
                    return -1;
                }
            }
            current_state = PLAYER_CMD_STOP;
            break;
            
        default:
            elog("Unknown player: command %d\n", cmd);
            return -1;
    }
    
    return 0;
}

/**
 * 释放PCM播放器资源
 */
static int pcmPlayer_deinit() {
    if (pa_handle) {
        int error;
        pa_simple_drain(pa_handle, &error);
        pa_simple_free(pa_handle);
        pa_handle = NULL;
    }

    if (context) {
        pa_context_disconnect(context);
        pa_context_unref(context);
        context = NULL;
    }

    if (mainloop) {
        pa_mainloop_free(mainloop);
        mainloop = NULL;
    }

    stream_index = PA_INVALID_INDEX;
    current_state = PLAYER_CMD_STOP;
    
    dlog("PulseAudio player deinitialized\n");
    return 0;
}

// PCM播放器接口结构体
static pcm_player_t pcm_player = {
    .platform = LINUX,
    .name = "PulseAudio",
    .check = check_pulseaudio_support,
    .init = pcmPlayer_init,
    .getParams = pcmPlayer_getParams,
    .setVolume = pcmPlayer_setVolume,
    .write = pcmPlayer_write,
    .ctrl = pcmPlayer_ctrl,
    .deinit = pcmPlayer_deinit,
};

/**
 * 自动注册PCM播放器
 */
__attribute__((constructor))
static void auto_register_pcm_player(void) {
    pcmPlayer_register(&pcm_player);
}